import tkinter as tk
from tkinter import ttk
import threading

from util.puzzle8_state import Puzzle8State
from util.puzzle8_solver_factory import Puzzle8SolverFactory, SolverMethod


class PuzzleWindow(tk.Tk):
    def __init__(self):
        super(PuzzleWindow, self).__init__()

        self.method_var = tk.StringVar()
        self.geometry('500x360')
        self.title("8数码游戏")

        self.solver = None
        self.timer = None

        self.cur_move_index = 0
        self.move_history_list = None
        self.move_history_len = 0

        self.main_frame = tk.Frame(self, width=480, height=350)
        self.main_frame.pack()

        self.init_origin_state_window();
        self.init_final_state_window();
        self.init_operate_window()

        self.origin_state = Puzzle8State()
        self.final_state = Puzzle8State();

    def init_origin_state_window(self):
        self.origin_state_frame = tk.Frame(master=self.main_frame, relief=tk.RAISED,
                                           borderwidth=1)

        # self.origin_state_frame.pack()
        self.origin_state_frame.place(x=20, y=50)

        title = tk.Label(master=self, text=f"初始状态")
        title.place(x=20, y=20);

        self.origin_state_grid = [];

        for i in range(3):
            for j in range(3):
                frame = tk.Frame(
                    master=self.origin_state_frame,
                    relief=tk.RAISED,
                    borderwidth=1
                )
                frame.grid(row=i, column=j, padx=5, pady=5)
                if i == 2 and j == 2:
                    label = tk.Label(master=frame, text=f" ")
                    label.pack(padx=5, pady=5)
                    self.origin_state_grid.append(label)
                else:
                    label = tk.Label(master=frame, text=f"{i * 3 + j + 1}")
                    label.pack(padx=5, pady=5)
                    self.origin_state_grid.append(label)

        random_button = tk.Button(master=self, text="随机生成", command=self.origin_random_generate);
        random_button.place(x=20, y=200)

        edit_button = tk.Button(master=self, text="编辑", command=self.origin_edit);
        edit_button.place(x=20, y=240)

        reset_button = tk.Button(master=self, text="重置", command=self.origin_reset);
        reset_button.place(x=20, y=280)

    def init_final_state_window(self):

        left = 180
        self.final_state_frame = tk.Frame(master=self.main_frame, relief=tk.RAISED,
                                          borderwidth=1)

        # self.final_state_frame.pack()
        self.final_state_frame.place(x=left, y=50)

        title = tk.Label(master=self, text=f"目标状态")
        title.place(x=left, y=20);

        self.final_state_grid = [];

        for i in range(3):
            for j in range(3):
                frame = tk.Frame(
                    master=self.final_state_frame,
                    relief=tk.RAISED,
                    borderwidth=1
                )
                frame.grid(row=i, column=j, padx=5, pady=5)
                if i == 2 and j == 2:
                    label = tk.Label(master=frame, text=f" ")
                    label.pack(padx=5, pady=5)
                    self.final_state_grid.append(label)
                else:
                    label = tk.Label(master=frame, text=f"{i * 3 + j + 1}")
                    label.pack(padx=5, pady=5)
                    self.final_state_grid.append(label)

        random_button = tk.Button(master=self, text="随机生成", command=self.final_random_generate);
        random_button.place(x=left, y=200)

        edit_button = tk.Button(master=self, text="编辑", command=self.final_edit);
        edit_button.place(x=left, y=240)

        reset_button = tk.Button(master=self, text="重置", command=self.final_reset);
        reset_button.place(x=left, y=280)

    def init_operate_window(self):
        left = 360

        self.operate_frame = tk.Frame(master=self.main_frame, relief=tk.RAISED,
                                      borderwidth=1)
        self.operate_frame.place(x=left, y=50)

        title = tk.Label(master=self, text=f"求解")
        title.place(x=left, y=20);

        select_solve_method_button = tk.Button(master=self.operate_frame, text="选择算法",
                                               command=self.select_solve_method);
        select_solve_method_button.pack()

        method_combo = ttk.Combobox(self.main_frame, textvariable=self.method_var, style="1.TCombobox", width=2,
                                    height=30)
        method_combo["value"] = ("BFS", "DFS", "AStar")
        method_combo.place(x=left + 70, y=50)

        solve_button = tk.Button(master=self.operate_frame, text="求解", command=self.solve);
        solve_button.pack()

        display_button = tk.Button(master=self.operate_frame, text="动画演示", command=self.animate_display);
        display_button.pack()

        prev_button = tk.Button(master=self.operate_frame, text="prev", command=self.prev_display);
        prev_button.pack()

        next_button = tk.Button(master=self.operate_frame, text="next", command=self.next_display);
        next_button.pack()

        reset_button = tk.Button(master=self.operate_frame, text="reset", command=self.reset_display);
        reset_button.pack()

    def origin_random_generate(self):
        cur_state = self.origin_state.random_generate()
        self.update_origin_state(cur_state)

    def origin_edit(self):
        pass

    def origin_reset(self):
        cur_state = self.origin_state.reset()
        self.update_origin_state(cur_state)

    def final_random_generate(self):
        cur_state = self.final_state.random_generate()
        self.update_final_state(cur_state)

    def final_edit(self):
        pass

    def final_reset(self):
        cur_state = self.final_state.reset()
        self.update_final_state(cur_state)

    def update_origin_state(self, cur_state, zero_highlight=False):
        for index, num in enumerate(cur_state):
            if num == 0:
                self.origin_state_grid[index].config(text=f" ")
                self.origin_state_grid[index].config(bg='red')
            else:
                self.origin_state_grid[index].config(text=f"{num}")
                self.origin_state_grid[index].config(bg='gray')

    def update_final_state(self, cur_state):
        self.final_state.cur_state_list = cur_state
        for index, num in enumerate(cur_state):
            if num == 0:
                self.final_state_grid[index].config(text=f" ")
            else:
                self.final_state_grid[index].config(text=f"{num}")

    def select_solve_method(self):
        solver_method = SolverMethod.BFSMethod

        cur_method = self.method_var.get()
        if cur_method == "DFS":
            solver_method = SolverMethod.DFSMethod
        if cur_method == "AStar":
            solver_method = SolverMethod.AStarMethod

        Puzzle8SolverFactory.instance().set_method(solver_method)
        self.solver = Puzzle8SolverFactory.instance().get_solver()

    def solve(self):
        self.solver.solve(self.origin_state, self.final_state)

        self.move_history_list = self.solver.get_move_history_list()
        self.move_history_len = len(self.move_history_list)
        self.cur_move_index = 0

    def animate_display(self):
        self.timer = threading.Timer(1, self.anim_display_result)
        self.timer.start()

    def anim_display_result(self):
        cur_move = self.move_history_list[self.cur_move_index]
        print(cur_move)
        self.update_origin_state(cur_move)
        self.cur_move_index += 1
        print(self.cur_move_index)
        if self.cur_move_index >= self.move_history_len:
            self.timer.cancel()
            return
        self.animate_display()

    def prev_display(self):
        self.cur_move_index -= 1
        if self.cur_move_index < 0:
            self.cur_move_index = 0;
        cur_move = self.move_history_list[self.cur_move_index]
        print(cur_move)
        self.update_origin_state(cur_move)

    def next_display(self):
        self.cur_move_index += 1
        if self.cur_move_index >= self.move_history_len:
            self.cur_move_index = self.move_history_len - 1
        cur_move = self.move_history_list[self.cur_move_index]
        print(cur_move)
        self.update_origin_state(cur_move)

    def reset_display(self):
        self.cur_move_index = 0

        cur_move = self.move_history_list[self.cur_move_index]
        print(cur_move)
        self.update_origin_state(cur_move)


puzzleWindow = PuzzleWindow();
puzzleWindow.mainloop()
